perm filename FREEFO.SAI[1,LES] blob sn#002753 filedate 1973-01-25 generic text, type T, neo UTF8
BEGIN  "FREEFOROL: 15 OCT 1969
"  COMMENT

OPERATION

Freeforol is a text macro processor that can be used to generate form
letters  and other fill-in-the-blanks text.  If you type R FREEFO, it
does the following.
1)  Requests via TTY the name of a TEXT FILE on the disk 
    containing a macro definition, as specified below.
2)  Requests an ARGUMENT FILE that is a text file on the disk
    containing lists of string arguments, as specified below.
    A null response (carriage return only) causes the balance of the
    text file to be searched for arguments.
3)  Requests a set of initial string values for arguments 0-9.
    Each argument is terminated by a carriage return.  A null
    response for any argument terminates the sequence.
4)  Requests the number of COPIES to be produced for each argument
    list.  A null response selects 1 copy.
5)  Requests an OUTPUT DEVICE and, if DSK or DTA, a file name.
    A null response selects the LPT.
6)  Produces the specified text.


PROCESS

The first character of the macro file is taken as a  delimiter.   The
text  macro  begins  just  after  the delimiter and ends at the first
occurence of <delimiter><non-parameter>, where <parameter> is a digit
or    letter    in    A-J.      In   between,   each   occurence   of
<delimiter><parameter> is  replaced  by  the  current  value  of  the
corresponding  argument.  "0" specifies the argument assigned in step
3,above.   "1" through "9" select the first through  ninth  arguments
of  each  list  that  follows, while "A" through "J" select the tenth
through twentieth arguments.

The <non-parameter> mentioned above and any characters  following  up
to  the  next  <delimiter>  will  be  used  as alternative separators
between arguments in the lists that follow.  Any line  numbers,  form
feeds,  or  carriage  returns will be ignored as separators, but line
feeds will be used.  After the separator string  comes  a  terminator
string,  followed  by  another <delimiter>.  If the terminator string
contains one or more characters, the first  character  only  will  be
used  to mark the end of the argument list.  If the terminator string
is null then the occurence of two separators in a row (i. e.  a  null
argument) will be used to terminate each list.

If  the  first  character following the <delimiter> at the end of the
terminator string is a TAB,then all parameters except 0 will  be  set
to  <null>  before  each  new set of arguments is read in. Otherwise,
these parameters will retain their old values.

On the same  line  following  the  terminator  string,  a  series  of
parameter names may (or may not) be given, separated by <delimiter>s.
They are given in order beginning with parameter 0 and,  if  present,
will  be  used to prompt the initial values in step 3, above.  In any
case, the argument lists begin on the next line, or at the  beginning
of the argument file if one was given in step 2 above.

Thus, the general form of the source file(s) is
	<delimiter>
	<macro body, including parameters>
	<delimiter><separator string><delimiter><terminator>
	<delimiter><optional TAB><optional name of argument 0><delimiter><arg1><del>...
	<argument lists>


EXAMPLES

For  example,  suppose  the  following  text  were  used,  with <TAB>
representing the horizontal tab character.
	@
	This is a@2 macro processor,
	but it is @1.
	@<TAB>@
	@unused@second phrase@adjective@
	fairly general<TAB> simple
	rather easy to understand<TAB>n elegant
It  is  seen  that @ is the delimiter and that the macro body ends at
the @ preceding the first <TAB>.  It is also seen that <TAB>  is  the
separator  and  <LINE FEED> is the terminator for the argument lists,
so the following output would be produced.

	This is a simple macro processor,
	but it is fairly general.

	This is an elegant macro processor,
	but it is fairly easy to understand.

Alternatively, if the source text were of the form
	@
	<macro body>
	@
	<TAB>@@<names of arguments,separated by @>
	<argument lists>
then  the arguments would be separated by either <TAB> or <LINE FEED>
and each list would be terminated by two of these in a row (e.  g.  a
blank line).


FEATURES

Several features should be noted.
1)  Any form feeds in the macro body are preserved, but they are
    ignored in the argument lists.
2)  Excess or unused arguments are ignored, so you can put comments
    in the argument lists that will not show up in the output.
3)  Whenever null arguments are given (two separators with nothing
    between), the old value of the argument is retained.  Similarly,
    if the argument list is terminated before some parameters are
    assigned new values, they retain their old values.  Thus, if
    repeating parameters are placed at the end, they need not be
    restated on each list.  You may defeat this feature and reset
    all arguments to <null> each time  by placing a TAB in front
    of the name of argument 0, as discussed above.
4)  If the automatic reset feature is not used, the initial values
    assigned to the arguments at run time hold until they are replaced
    from the lists (if ever).  Note that parameter 0 is never assigned
    a value from the lists and so may be used, for example, to enter a
    date at run time that applies to the entire run.
5)  If you wish to use just one set of arguments, you may enter them
    all at run time provided that you use an argument list containing
    no arguments;

DEFINE THIS="COMMENT", CR="'15", LF="'12", HT="'11", FF="'14",
	ALT="'33", CRLF="CR&LF", NOFF="CR&'177&'21",
	TYPE(TEXT)="OUT(TTY,""TEXT"")", TYPIN="INPUT(TTY,LINE)",
	ERR(MES,NEXT)="BEGIN OUT(TTY,""MES""&CRLF); GO TO NEXT END";

DEFINE NOVA="25";  THIS IS MAXIMUM NUMBER OF STRING SUBSTITUTIONS;
DEFINE ARGS="20";  THIS IS THE MAXIMUM NUMBER OF AGRUMENTS;
DEFINE TTY="1", SOURCE="2", SINK="3", LINE="1", MARK="2", CHAR="3";

STRING ARRAY TEXT[0:NOVA];  THIS HOLDS SEGMENTS OF MACRO TEXT;
STRING ARRAY VAL[0:ARGS];  THIS HOLDS CURRENT ARGUMENT VALUES;
STRING DEV, SEP, FILE, S;  LABEL SORRY, CHOOSE, REOPEN, ARF ;
INTEGER BRK, EOF, OOF, T, V, I, J, COPY, TERM, RESET;

	THIS INITIALIZES I/O;
BREAKSET(LINE,LF,"I");  BREAKSET(LINE,CR&FF,"O");  BREAKSET(LINE,NULL,"N");
BREAKSET(CHAR,CR&FF,"X");  BREAKSET(CHAR,CR&FF,"O");  THIS GETS ONE CHARACTER;
BREAKSET(CHAR,NULL,"N");  BREAKSET(CHAR,NULL,"A");
OPEN(TTY,"TTY",1,1,1,100,BRK,EOF);  OPEN(SOURCE,"DSK",1,2,0,4000,BRK,EOF);

CHOOSE:  TYPE(TEXT FILE:  );  EOF←1;
LOOKUP(SOURCE,TYPIN,EOF);  IF EOF THEN ERR(FILE NOT FOUND,CHOOSE);

	THIS READS AND SEGMENTS THE TEXT MACRO;
BREAKSET(MARK,INPUT(SOURCE,CHAR),"I");  BREAKSET(MARK,NULL,"N");  T←0;
WHILE T<2 OR I←TEXT[T-1]≥"0" AND I≤"9" OR I≥"A" AND I≤"J" DO
	BEGIN IF T>NOVA  THEN ERR(TOO MANY SUBSTITUTIONS.,SORRY);
	TEXT[T]←INPUT(SOURCE,MARK);  T←T+1;
	END;

	THIS FINDS THE SEPARATORS AND TERMINATOR;
BREAKSET(MARK,CR&FF,"O");  DEV←INPUT(SOURCE,MARK);  THIS GETS TERMINATOR;
TERM←DEV;  S←TEXT[T-1]; 
SEP←SCAN(S,MARK,BRK)& (IF TERM=0 THEN NULL ELSE TERM);
	THIS GETS SEPARATORS AND TERMINATOR;
RESET←DEV←INPUT(SOURCE,LINE);  THIS PICKS UP REST OF LINE;

	THIS GETS ARGUMENT FILE, IF DIFFERENT;
ARF:  TYPE("ARGUMENT FILE, IF ANY:  ");  FILE←TYPIN;
IF LENGTH(FILE)>0 THEN
	BEGIN  EOF←1;  LOOKUP(SOURCE,FILE,EOF);
	IF EOF THEN ERR(FILE NOT FOUND.,ARF);
	END;

	THIS INITIALIZES ARGUMENTS 0 THROUGH 9;
TYPE(INITIAL ARGUMENTS:
);  I←0;
WHILE I=0 OR I<10 AND LENGTH(VAL[I-1])>0 DO
	BEGIN  OUT(TTY,(I+"0")&") "&
		(IF LENGTH(DEV)>0 THEN SCAN(DEV,MARK,BRK) ELSE NULL)&":  ");
	VAL[I]←TYPIN;  I←I+1;
	END;

TYPE(COPIES:  );  COPY←CVD(TYPIN);  IF COPY<1 THEN COPY←1;

	THIS GETS OUTPUT DEVICE;
REOPEN:  TYPE(OUTPUT DEVICE:  );  DEV←TYPIN;
IF LENGTH(DEV)=0 THEN DEV←"LPT";  OOF←1;  OPEN(SINK,DEV,1,0,2,0,BRK,OOF);
IF OOF THEN ERR(DEVICE NOT AVAILABLE.,REOPEN);
IF DEV="D" OR DEV="d"  THEN
	BEGIN  LABEL DEFILE;  DEFILE:
	TYPE(OUTPUT FILE NAME:  );  ENTER(SINK,TYPIN,OOF);
	IF OOF THEN ERR(TRY AGAIN,DEFILE)
	END;

	THIS SCANS ARGUMENT LISTS AND OUTPUTS TEXT;
BREAKSET(MARK,SEP,"I");  THIS SETS SCAN TO STOP ON SEPARATORS OR TERMINATOR;
WHILE NOT EOF DO
	BEGIN
	IF RESET=HT THEN FOR V←1 STEP 1 UNTIL ARGS DO VAL[V]←NULL;
	V←0;
	WHILE V=0 OR  TERM>0 AND TERM ≠ BRK OR TERM=0 AND LENGTH(S)>0 DO
		BEGIN IF V<ARGS THEN V←V+1;
		S←INPUT(SOURCE,MARK);  IF LENGTH(S)>0 THEN VAL[V]←S;
		IF EOF AND TERM>0 THEN GO TO SORRY;
		END;
	FOR V←1 STEP 1 UNTIL COPY DO
		BEGIN  OUT(SINK,TEXT[0]);
		FOR I←1 STEP 1 UNTIL T-2 DO OUT(SINK,VAL[IF J←TEXT[I]<"A"
			THEN J-"0" ELSE J-"A"+10]&TEXT[I][2 TO INF]);
		END
	END;
SORRY:  CLOSE(SINK);  CLOSE(SOURCE)
END;